'written by Torbjoern Haugen www.softswit.ch
'27. aug 2010
'-got good perf vs bruteforce (8 vs 30) millis @ 100 objs
'-todo: Better design, cleaner code. Less cruft. Make it reusable
SuperStrict


Framework brl.GLMax2D
Import brl.random
Import brl.standardio


Include "../common/tvector.bmx"
Include "tgrid.bmx"

'SetGraphicsDriver GLMax2DDriver()
Global xres# = 640
Global yres# = 480


local mem_at_init:int = gcmemalloced()

Graphics xres,yres,0,0,2

Local tim:Int, oldtim:Int
Local dt#

'------------------------------------------------
Const MAX_BOXES:Int = 200
Global aGrid:TGrid = Null
Global displayObjects:TList = New TList
Global collisionPair:Int[MAX_BOXES,MAX_BOXES]
'------------------------------------------------

cls()
drawtext "placing rects at random positions. Please wait...", graphicswidth()/4, graphicsheight()/2
flip()
setup()

While Not AppTerminate() And Not KeyHit(KEY_ESCAPE)
	Cls()
	
	Local perfColl1:Int, perfColl2:Int
	Local perfTot1:Int, perfTot2:Int
	Local perfDraw1:Int, perfDraw2:Int
	Local perfReaction:Int
	Local perfIntersecting:Int
	

	tim = MilliSecs()
	Local dtmillis:Int = (tim - oldtim)
	dt = dtmillis / 1000.0 'nice to have a float version too
	oldtim = tim
	
	Local iters:Int = 50
	Local delta# = 1.0 / Float(iters)
	
	perfColl1 = MilliSecs()
	For Local i:Int = 0 Until iters
		'clearCollisionPairs()
		aGrid.initMap()
		
		If(1)
			Local intersectingStart:Int = MilliSecs()
			Local collideTestList:TList = aGrid.getIntersecting()
			perfIntersecting :+ MilliSecs()-intersectingStart
			
			Local reactStart:Int = MilliSecs()
			smartForce(collideTestList)
			perfReaction :+ MilliSecs()-reactStart
		Else
			bruteForce()
		EndIf
		
		
		moveParticles(delta)
	
	Next
	perfColl2 = MilliSecs()
	
	perfDraw1 = MilliSecs()
	aGrid.drawGrid()
	drawparticles()
	perfDraw2 = MilliSecs()
	
	SetColor 255,255,255
	DrawText "collision:" + (perfColl2-perfColl1), 15, 15
	DrawText "draw     :" + (perfDraw2-perfDraw2), 15,15*2
	DrawText "total    :" + dtmillis, 15,15*3
	DrawText "fps    :" + (1000.0/dtmillis), 15,15*4
	DrawText "time used in finding intersecting tot:"+ perfIntersecting  , 15, 15*5
	DrawText "time used on reaction            :"+ perfReaction, 15, 15*7
	DrawText "k mem used:"+ (GCMemAlloced()/1024) , 15, 15*9
	drawtext "collision pairs:" + agrid.collisionpairs.count(), 15,15*10
	perfReaction = 0
	perfIntersecting  = 0
	'GCResume()
	'GCCollect()
	'GCSuspend()
	Flip
Wend

print "end mem - start: " + (gcmemalloced() - mem_at_init)/1024 + " k"

end

Type TMovingBox Extends TBoundingBox
	Global gBoxCount:Int = 0 'keep a count over the number of boxes so ... hm. this is bad for dynamic stuff where entites come and go.. 
	Field id:Int
	Field dir:TVector
	Field collided:Int
	Field collisions:TList = New TList
	
	Method New()
		id = gBoxCount
		gBoxCount :+1
		
		Local angle# = Rand(0,360)
		Local spd# = 1.0
		dir = VectorCreate( Cos(angle)*spd, Sin(angle)*spd )
	End Method
End Type




Function clearCollisionPairs()
'	For Local b:TMovingBox = EachIn displayObjects
'		b.collisions.clear()
'	Next
	
	For Local i:Int = 0 Until MAX_BOXES
	For Local j:Int = 0 Until MAX_BOXES
		collisionPair[i,j] = False
	Next
	Next
End Function


Function bruteForce()
	For Local a:TMovingBox = EachIn displayObjects
	For Local b:TMovingBox = EachIn displayObjects
		If a=b Continue

      'Local haveCollided:Int = collisionPair[a.id, b.id] Or collisionPair[b.id, a.id] 
      'If(Not haveCollided )
      'If a.collided = False And b.collided = b.collided = False
        If( boxesIntersect(a,b) )
          'swap dirs
          Local tmp:TVector = a.dir
          a.dir = b.dir
          b.dir = tmp
			  a.collided = True
			  b.collided = True

          'collisionPair[a.id, b.id] = True  
          'collisionPair[b.id, a.id] = True   
			EndIf
      'EndIf
	Next
	Next
End Function

Function smartForce(pairs:TList) 'list of collision pairs
	For Local p:TCollisionPair = EachIn pairs
		Local a:TMovingBox = TMovingBox(p.a) 'first, second?
		Local b:TMovingBox = TMovingBox(p.b)
		
		Assert a<>b, "collision pair a,b are same obj"

		'make sure they havnt collided before
      'Local haveCollided:Int = collisionPair[a.id, b.id] Or collisionPair[b.id, a.id]
      If a.collided = False Or b.collided = False '(Not haveCollided )
        If 1'( boxesIntersect(a,b) )
				'swap dirs
				Local tmp:TVector = a.dir
				a.dir = b.dir
				b.dir = tmp
				a.collided = True
				b.collided = True
				
				collisionPair[a.id, b.id] = True  
				collisionPair[b.id, a.id] = True   
			EndIf
      EndIf
	Next
End Function



Function moveParticles(dt:Float)
  For Local b:TMovingBox = EachIn displayObjects
    b.xmin :+ b.dir._x * dt
    b.ymin :+ b.dir._y * dt
    b.xmax :+ b.dir._x * dt
    b.ymax :+ b.dir._y * dt

    'Bounce screen corners
    If(b.xmin < 5) b.dir._x :* -1.0
    If(b.ymin < 5) b.dir._y :* -1.0
    If(b.xmax > (xres-5) ) b.dir._x :* -1.0
    If(b.ymax > (yres-5) ) b.dir._y :* -1.0
  Next
End Function

Function drawParticles()
   SetColor(255,255,255)
   For Local b:TMovingBox = EachIn displayObjects
      If( b.collided )
        SetColor(255,0,0)
      Else
        SetColor(0,255,0)
      EndIf

      DrawRect(b.xmin, b.ymin, b.xmax-b.xmin, b.ymax-b.ymin)
      b.collided = False
   Next
End Function

'Make boxes with random position and direction
Function setup()
	aGrid = TGrid.Create(0,0,xres,yres,10,10)

	For Local i:Int = 0 Until MAX_BOXES
		Local b:TMovingBox = New TMovingBox
		displayObjects.addFirst(b)
		aGrid.insertBox(b)
	Next
	
	

	
	Local xlower#, xupper#, ylower#, yupper#
	
	xlower = 10.0
	xupper = xres-xlower
	
	ylower = 10.0
	yupper = yres-ylower

	'// Position a box, if it intersects with one allready placed, try again
	Local boxesMade:Int = 0
	For Local b:TBoundingBox = EachIn displayObjects
		Local boxIntersecting:Int = True
		
		While( boxIntersecting )
			boxIntersecting = False
			b.xmin = Rnd(xlower,xupper) 
			b.ymin = Rnd(ylower,yupper) 
			Local size# = Rnd(5,10)
			b.xmax = b.xmin + size
			b.ymax = b.ymin + size
			
			For Local i:Int = 0 Until boxesMade
				If(b = displayObjects.valueAtIndex(i) ) Continue
				
				If boxesIntersect(b, TBoundingBox( displayObjects.valueAtIndex(i) ) )
					boxIntersecting = True
				EndIf
				
			Next
			If(boxIntersecting = False) boxesMade :+ 1
		Wend
	Next
	
End Function